/*
** Copyright (C) 2010 Calin Radoni
**
** This file is part of CHScanner.
**
** CHScanner is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** CHScanner is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with CHScanner. If not, see <http://www.gnu.org/licenses/>.
*/

#include "StdAfx.h"
#include ".\thescanner.h"

#include "ScanMessages.h"

//---------------------------------------------------------------------------

TheScanner *mainScanner = NULL;

//---------------------------------------------------------------------------

TheScanner::TheScanner(void)
{
	isRunning = false;
	stopStopStop  = false;

	config.Initialize();

	eventDone = NULL;

	mainScanner = this;

	msgWnd = NULL;

	scanMethodFin.SetType(__wmtFin);
	scanMethodNull.SetType(__wmtNull);
	scanMethodXmas.SetType(__wmtXmas);

	scanMethod6Fin.SetType(__wmt6Fin);
	scanMethod6Null.SetType(__wmt6Null);
	scanMethod6Xmas.SetType(__wmt6Xmas);

	startTime = 0;

	systemArpCacheUpdated = false;

	applicationPath.Empty();
	defaultInterfaceName.Empty();
	appCfg = NULL;

	automaticMode = false;

	xHuntParams.Initialize();
}

TheScanner::~TheScanner(void)
{
	if(eventDone!=NULL){
		SetEvent(eventDone);
		CloseHandle(eventDone);
		eventDone = NULL;
	}
}

//---------------------------------------------------------------------------

void TheScanner::SendStatusMessage(UINT uMsgIn, WPARAM wpIn, LPARAM lpIn)
{
	if(msgWnd==NULL) return;

	SendMessage(msgWnd, uMsgIn, wpIn, lpIn);
}

void TheScanner::PostStatusMessage(UINT uMsgIn, WPARAM wpIn, LPARAM lpIn)
{
	if(msgWnd==NULL) return;

	PostMessage(msgWnd, uMsgIn, wpIn, lpIn);
}

//---------------------------------------------------------------------------

DWORD TheScanner::ComputeDeltaTime(DWORD dsIn, DWORD de)
{
DWORD ds, res;

	ds = dsIn;

	if(de<ds){
		// tick count has overlapped
		res = de;
		while(ds>0){
			ds++;
			res++;
		}
	}
	else{
		res = de - ds;
	}
	return res;
}

//---------------------------------------------------------------------------

void TheScanner::MemorizeStartTime(void)
{
	_tzset();
	_time64(&startTime);
}

//---------------------------------------------------------------------------
int TheScanner::RefreshAndExit(UINT uMsg, int retVal)
{
	DWORD scanEndTime;
	DWORD scanTime;

	scanEndTime = GetTickCount();
	scanTime = ComputeDeltaTime(scanStartTime, scanEndTime);

	listAddrs.Clear();
	listPorts.Clear();

	isRunning = false;

	if(eventDone!=NULL)
		SetEvent(eventDone);

	SendStatusMessage(uMsg, 0, (LPARAM)scanTime);

	return retVal;
}

//---------------------------------------------------------------------------

void TheScanner::FillDnsServerMAC(BYTE* buffIn)
{
	scanMethodArp.FillDnsServerMAC(buffIn);
}

//---------------------------------------------------------------------------

void TheScanner::UpdateSystemArpCache(WorkPhaseType phaseIn, WorkMethodType methodIn)
{
	MFullAddress* addr;
	MMachineInfo* mmi;
	int i, cnt;
	bool needUpdate;
	bool res;

	if(systemArpCacheUpdated) return;

	switch(methodIn){
		case __wmtNetBios :
			needUpdate = true;
			break;

		default:
			needUpdate = false;
	}
	if(!needUpdate) return;

	// Flush system's ARP Table
	if(!winPcap.ipHelper.FlushArpTable()){
		SendStatusMessage(CHS_MSG_SYSARPTAB_UPDATED, (WPARAM)0, (LPARAM)0);
		return;
	}

	// Add entries in the system's ARP Table
	res = true;
	if(phaseIn==__wptDiscovery){
		cnt = listAddrs.GetSize();
		for(i=0; i<cnt && res; i++){
			addr = listAddrs.GetByIdx(i);
			if(addr!=NULL){
				if(!addr->gwMAC){
					if(!addr->addrMAC.IsEmpty()){
						res &= winPcap.ipHelper.AddArpTableEntry(addr->addrIP.GetD(), addr->addrMAC.addr);
					}
				}
			}
		}
	}
	if(phaseIn==__wptScan){
		cnt = machineList.GetSize();
		for(i=0; i<cnt && res; i++){
			mmi = machineList.GetByIdx(i);
			if(mmi!=NULL){
				if(!mmi->address.gwMAC){
					if(!mmi->address.addrMAC.IsEmpty()){
						res &= winPcap.ipHelper.AddArpTableEntry(mmi->address.addrIP.GetD(), mmi->address.addrMAC.addr);
					}
				}
			}
		}
	}

	if(res){
		systemArpCacheUpdated = true;
		SendStatusMessage(CHS_MSG_SYSARPTAB_UPDATED, (WPARAM)1, (LPARAM)0);
	}
	else{
		SendStatusMessage(CHS_MSG_SYSARPTAB_UPDATED, (WPARAM)0, (LPARAM)0);
	}
}

//---------------------------------------------------------------------------

int TheScanner::StartScanningMethod(
						MWinPcap* winPcapIn, WorkCfg* cfgIn, HANDLE eventIn, WorkMethodType methodType, CString dataIn,
						ListOfAddresses* addresses, MMachineInfoList *machInfoList, WorkPhaseType phaseIn, HWND hWndIn)
{
	ScanStartParameters ssp;
	int result;

	abortReason = MSR_ERR_OK;

	ssp.sspWinPcap              = winPcapIn;
	ssp.sspPackBuilderEx        = &packetBuilderEx;
	ssp.sspPackBuilderEx6       = &packetBuilderEx6;
	ssp.sspPacketBuilderPromisc = &packetBuilderPromisc;
	ssp.sspWorkCfg              = cfgIn;
	ssp.sspData                 = dataIn;
	ssp.sspArpCacheList         = &arpCacheList;
	ssp.sspListOfAddr           = addresses;
	ssp.sspMachInfoList         = machInfoList;
	ssp.sspWorkPhase            = phaseIn;
	ssp.sspMsgWnd               = hWndIn;
	ssp.sspWmiWorker            = &wmiWorker;
	if(appCfg!=NULL){
		ssp.sspNotifNetBios = appCfg->notifNetBios;
		ssp.sspNotifPing    = appCfg->notifPing;
		ssp.sspNotifTcp     = appCfg->notifTcp;
		ssp.sspNotifUdp     = appCfg->notifUdp;
	}
	else{
		ssp.sspNotifNetBios = 100;
		ssp.sspNotifPing    = 1000;
		ssp.sspNotifTcp     = 1000;
		ssp.sspNotifUdp     = 100;
	}

	switch(methodType){
		case __wmtNone:
			if(eventIn!=NULL)
				SetEvent(eventIn);
			return MSR_ERR_OK;

		case __wmtArp :
			result = scanMethodArp.Start(&ssp);
			abortReason = scanMethodArp.abortReason;
			break;

		case __wmtPing:
			result = scanMethodPing.Start(&ssp);
			abortReason = scanMethodPing.abortReason;
			break;

		case __wmtDns:
			result = scanMethodDns.Start(&ssp);
			abortReason = scanMethodDns.abortReason;
			break;

		case __wmtSyn :
			result = scanMethodSyn.Start(&ssp);
			abortReason = scanMethodSyn.abortReason;
			break;

		case __wmtAck :
			result = scanMethodAck.Start(&ssp);
			abortReason = scanMethodAck.abortReason;
			break;

		case __wmtFin :
			result = scanMethodFin.Start(&ssp);
			abortReason = scanMethodFin.abortReason;
			break;

		case __wmtNull :
			result = scanMethodNull.Start(&ssp);
			abortReason = scanMethodNull.abortReason;
			break;

		case __wmtXmas :
			result = scanMethodXmas.Start(&ssp);
			abortReason = scanMethodXmas.abortReason;
			break;

		case __wmtUdpSend :
			result = scanMethodUdp.Start(&ssp);
			abortReason = scanMethodUdp.abortReason;
			break;

		case __wmtIpProtocol :
			result = scanMethodIpProto.Start(&ssp);
			abortReason = scanMethodIpProto.abortReason;
			break;

		case __wmtNetBios :
			result = scanMethodNetBios.Start(&ssp);
			abortReason = scanMethodNetBios.abortReason;
			break;

		case __wmtWakeOnLAN :
			result = scanMethodWOL.Start(&ssp);
			abortReason = scanMethodWOL.abortReason;
			break;

		case __wmtWinSnmp :
			result = scanMethodSnmp.Start(&ssp);
			abortReason = scanMethodSnmp.abortReason;
			break;

		case __wmtDhcpDiscov :
			result = scanMethodDHCP.Start(&ssp);
			abortReason = scanMethodDHCP.abortReason;
			break;

		case __wmtPromisc :
			result = scanMethodPromisc.Start(&ssp);
			abortReason = scanMethodPromisc.abortReason;
			break;

		case __wmt4PingBcast :
			result = scanMethod4PingBcast.Start(&ssp);
			abortReason = scanMethod4PingBcast.abortReason;
			break;

		case __wmt6PingBcast :
			result = scanMethod6PingBcast.Start(&ssp);
			abortReason = scanMethod6PingBcast.abortReason;
			break;

		case __wmt6ND :
			result = scanMethod6ND.Start(&ssp);
			abortReason = scanMethod6ND.abortReason;
			break;

		case __wmt6MLD :
			result = scanMethod6MLD.Start(&ssp);
			abortReason = scanMethod6MLD.abortReason;
			break;

		case __wmt6Ping :
			result = scanMethod6Ping.Start(&ssp);
			abortReason = scanMethod6Ping.abortReason;
			break;

		case __wmt6Syn :
			result = scanMethod6Syn.Start(&ssp);
			abortReason = scanMethod6Syn.abortReason;
			break;

		case __wmt6Ack :
			result = scanMethod6Ack.Start(&ssp);
			abortReason = scanMethod6Ack.abortReason;
			break;

		case __wmt6Fin :
			result = scanMethod6Fin.Start(&ssp);
			abortReason = scanMethod6Fin.abortReason;
			break;

		case __wmt6Null :
			result = scanMethod6Null.Start(&ssp);
			abortReason = scanMethod6Null.abortReason;
			break;

		case __wmt6Xmas :
			result = scanMethod6Xmas.Start(&ssp);
			abortReason = scanMethod6Xmas.abortReason;
			break;

		case __wmt6UdpSend :
			result = scanMethod6Udp.Start(&ssp);
			abortReason = scanMethod6Udp.abortReason;
			break;

		case __wmt6Proto :
			result = scanMethod6Proto.Start(&ssp);
			abortReason = scanMethod6Proto.abortReason;
			break;

		case __wmtWMI :
			result = scanMethodWMI.Start(&ssp);
			abortReason = scanMethodWMI.abortReason;
			break;

		case __wmtIgmp :
			result = scanMethodIgmp.Start(&ssp);
			abortReason = scanMethodIgmp.abortReason;
			break;

		case __wmtShutdown :
			result = scanMethodShutdown.Start(&ssp);
			abortReason = scanMethodShutdown.abortReason;
			break;

		case __wmtHunt :
			scanMethodHunt.xHuntParams.LoadFromPointer(&xHuntParams);
			result = scanMethodHunt.Start(&ssp);
			abortReason = scanMethodHunt.abortReason;
			break;

		case __wmtPassive :
			result = scanMethodPassive.Start(&ssp);
			abortReason = scanMethodPassive.abortReason;
			break;

		default:
			if(eventIn!=NULL)
				SetEvent(eventIn);
			return -2;
	}

	return result;
}

int TheScanner::StartScanPhase(WorkPhaseType phase)
{
	WorkCfgElement* cfgElem;
	int i, cnt;
	int retVal;

	if(phase==__wptArp){
		retVal = StartScanningMethod(&winPcap, &config, eventDone, __wmtArp, "", &listAddrs, &machineList, phase, msgWnd);
		if(retVal){
//			retVal = StartScanningMethod(&winPcap, &config, eventDone, __wmt6PingBcast, "", &listAddrs, &machineList, phase, msgWnd);
			retVal = StartScanningMethod(&winPcap, &config, eventDone, __wmt6ND, "", &listAddrs, &machineList, phase, msgWnd);
		}
		return retVal;
	}

	retVal = MSR_ERR_OK;
	cnt = config.GetJobCount();
	for(i=0; i<cnt && retVal==MSR_ERR_OK; i++){
		cfgElem = config.GetJobElement(i);
		if(cfgElem!=NULL){
			if(cfgElem->phaseType==phase){
				UpdateSystemArpCache(phase, cfgElem->methodType);

				SendStatusMessage(CHS_MSG_METHOD_START, (WPARAM)0, (LPARAM)cfgElem->methodType);

				retVal = StartScanningMethod(
								&winPcap,
								&config,
								eventDone,
								cfgElem->methodType,
								cfgElem->data,
								&listAddrs,
								&machineList,
								phase,
								msgWnd);

				if(retVal==MSR_ERR_OK && abortReason!=MSR_ERR_OK)
					SendStatusMessage(CHS_MSG_METHOD_ABORT, (WPARAM)abortReason, (LPARAM)cfgElem->methodType);

				if(phase==__wptScan)
					SendStatusMessage(CHS_MSG_UPDATE_DATA, 0, 0);
			}
		}
	}

	return retVal;
}

//---------------------------------------------------------------------------

int TheScanner::Initialize(WorkCfg* wcIn)
{
	if(isRunning) return MSR_ERR_SCAN_IN_PROGRESS;

	if(eventDone==NULL){
		eventDone = CreateEvent(NULL, true, false, NULL);
		if(eventDone==NULL)
			return MSR_ERR_FAILED;
	}
	if(!ResetEvent(eventDone))
		return MSR_ERR_FAILED;

	if(!winPcap.GetIpInterface())
		return MSR_ERR_GET_IP_INTERFACE;

	if(!config.LoadFromPointer(wcIn))
		return MSR_ERR_PARAMETER;

	listAddrs.Clear();
	listPorts.Clear();

	stopStopStop = false;

	scanStartTime = GetTickCount();

	machineList.Clear();

	return MSR_ERR_OK;
}

//---------------------------------------------------------------------------

int TheScanner::RebuildAddrListByMAC(void)
{
	ListOfAddresses listAlive;
	MFullAddress* addr;
	int i, cnt;
	int retVal;

	cnt = listAddrs.GetSize();
	for(i=0; i<cnt; i++){
		addr = listAddrs.GetByIdx(i);
		if(addr!=NULL){
			if(!addr->addrMAC.IsEmpty()){
				retVal = listAlive.AddFullAddress(addr);
				if(retVal!=MSR_ERR_OK)
					return retVal;
			}
		}
	}

	listAddrs.Clear();

	cnt = listAlive.GetSize();
	for(i=0; i<cnt; i++){
		addr = listAlive.GetByIdx(i);
		retVal = listAddrs.AddFullAddress(addr);
		if(retVal!=MSR_ERR_OK)
			return retVal;
	}

	listAlive.Clear();

	return MSR_ERR_OK;
}

void TheScanner::CleanUpAliveFlag(void)
{
	MFullAddress* addr;
	int i, cnt;

	cnt = listAddrs.GetSize();
	for(i=0; i<cnt; i++){
		addr = listAddrs.GetByIdx(i);
		if(addr!=NULL){
			addr->alive = false;
		}
	}
}

int TheScanner::RebuildAddrListByAlive(void)
{
	ListOfAddresses listAlive;
	MFullAddress* addr;
	int i, cnt;
	int retVal;

	cnt = listAddrs.GetSize();
	for(i=0; i<cnt; i++){
		addr = listAddrs.GetByIdx(i);
		if(addr!=NULL){
			if(addr->alive){
				retVal = listAlive.AddFullAddress(addr);
				if(retVal!=MSR_ERR_OK)
					return retVal;
			}
		}
	}

	listAddrs.Clear();

	cnt = listAlive.GetSize();
	for(i=0; i<cnt; i++){
		addr = listAlive.GetByIdx(i);
		retVal = listAddrs.AddFullAddress(addr);
		if(retVal!=MSR_ERR_OK)
			return retVal;
	}

	listAlive.Clear();

	return MSR_ERR_OK;
}

//---------------------------------------------------------------------------

int TheScanner::PopulateMachineList(void)
{
	MFullAddress *addr;
	MMachineInfo mach;
	MMachineInfo *oldMach;
	in_addr6 ia6;
	int i, cnt;

	cnt = listAddrs.GetSize();
	for(i=0; i<cnt; i++){
		addr = listAddrs.GetByIdx(i);
		if(addr!=NULL){
			if(!addr->addrIP.IsEmpty())
				oldMach = machineList.GetMachineInfoByIpAddr(addr->addrIP.GetD());
			else{
				addr->addrIPv6.Get(&ia6);
				oldMach = machineList.GetMachineInfoByIpAddr6(&ia6);
			}

			if(oldMach!=NULL){
				oldMach->address.LoadFromPointer(addr);
			}
			else{
				mach.Initialize();
				mach.address.LoadFromPointer(addr);
				if(!machineList.Add(&mach))
					return MSR_ERR_MEMORY_ALLOC_ERROR;
			}
		}
	}

	// I do not need this anymore !
	listAddrs.Clear();

	return MSR_ERR_OK;
}

//---------------------------------------------------------------------------

int TheScanner::ScanPhaseWrapper(WorkPhaseType wptIn)
{
	int retVal;

	if(wptIn==__wptNone) return MSR_ERR_OK;

	if(wptIn!=__wptArp){
		if(config.GetJobsCount(wptIn)==0){
			// no jobs, skipping
			SendStatusMessage(CHS_MSG_SCANPHASE_SKIPPED, (WPARAM)wptIn, 0);
			return MSR_ERR_OK;
		}
	}

	SendStatusMessage(CHS_MSG_SCANPHASE_START, (WPARAM)wptIn, 0);
	retVal = StartScanPhase(wptIn);
	if(retVal<0)
		SendStatusMessage(CHS_MSG_SCANPHASE_FAILED, (WPARAM)wptIn, 0);
	else
		SendStatusMessage(CHS_MSG_SCANPHASE_DONE, (WPARAM)wptIn, 0);

	return retVal;
}

//---------------------------------------------------------------------------

void TheScanner::StartScanPassiveMode(void)
{
	StartScanningMethod(&winPcap, &config, eventDone, __wmtPassive, "", &listAddrs, &machineList, __wptNone, msgWnd);
}

//---------------------------------------------------------------------------

void TheScanner::StartScanHuntMode(void)
{
	StartScanningMethod(&winPcap, &config, eventDone, __wmtHunt, "", &listAddrs, &machineList, __wptNone, msgWnd);
}

//---------------------------------------------------------------------------

int TheScanner::StartScan(void)
{
	CString str;
	int retVal;

	WorkCfgElement* cfgElem;
	int i, cnt;
	bool credentialsNeeded;
	bool wmiFound;

	isRunning = true;

	///< prepare the field

	MemorizeStartTime();

	systemArpCacheUpdated = false;

	if(config.xArpCacheOp!=__acoIgnore){
		if(!applicationPath.IsEmpty()){
			str = applicationPath;
			str += _T("arpCache.dat");
			arpCacheList.LoadFromFile(str);
		}
	}

	scanStartTime = GetTickCount();

	if(config.xHuntMode){
		SendStatusMessage(CHS_MSG_SCAN_START, 2, 0);
		retVal = MSR_ERR_OK;
		StartScanHuntMode();
		return RefreshAndExit(CHS_MSG_SCAN_DONE, retVal);
	}

	if(config.xPassiveMode){
		SendStatusMessage(CHS_MSG_SCAN_START, 1, 0);
		retVal = MSR_ERR_OK;
		StartScanPassiveMode();
		return RefreshAndExit(CHS_MSG_SCAN_DONE, retVal);
	}

	SendStatusMessage(CHS_MSG_SCAN_START, 0, 0);

	if(!listAddrs.LoadFromString(config.xAddresses))
		return RefreshAndExit(CHS_MSG_SCAN_DONE, MSR_ERR_PARAMETER);

	retVal = listAddrs.GetSize4() + listAddrs.GetSize6();
	if(retVal==0){
		retVal = MSR_ERR_OK;
		SendStatusMessage(CHS_MSG_SCAN_SKIPPED, 0, 0);
		return RefreshAndExit(CHS_MSG_SCAN_DONE, retVal);
	}

	SendStatusMessage(CHS_MSG_LIST_BUILDED, 0, (LPARAM)retVal);

	switch(config.xScanOrder){
		case __soNumeric :
			listAddrs.SortTheList();
			SendStatusMessage(CHS_MSG_SCAN_SORT_ADDRLIST, 0, 0);
			break;
		case __soRandomized :
			listAddrs.RandomizeList();
			SendStatusMessage(CHS_MSG_SCAN_RANDOMIZE_ADDRLIST, 0, 0);
			break;
	}

	if(stopStopStop) return RefreshAndExit(CHS_MSG_SCAN_DONE, MSR_ERR_USER_STOPPED);

	///< ARP "scanning"

	retVal = ScanPhaseWrapper(__wptArp);
	if(retVal!=MSR_ERR_OK)
		return RefreshAndExit(CHS_MSG_SCAN_DONE, retVal);

	if(stopStopStop) return RefreshAndExit(CHS_MSG_SCAN_DONE, MSR_ERR_USER_STOPPED);

	BYTE dnsMAC[6];
	FillDnsServerMAC(dnsMAC);
	winPcap.SetDnsMAC(dnsMAC);

	retVal = RebuildAddrListByMAC();
	if(retVal!=MSR_ERR_OK)
		return RefreshAndExit(CHS_MSG_SCAN_DONE, retVal);

	SendStatusMessage(CHS_MSG_AFTER_PHASE, 0, (LPARAM)listAddrs.GetSize());

	if(!config.xShowArpDiscoveredHosts){
		CleanUpAliveFlag();
	}

	///< Discovery

	retVal = ScanPhaseWrapper(__wptDiscovery);
	if(retVal!=MSR_ERR_OK)
		return RefreshAndExit(CHS_MSG_SCAN_DONE, retVal);

	// select only alive hosts
	if(!config.xScanAll){
		retVal = RebuildAddrListByAlive();
		if(retVal!=MSR_ERR_OK)
			return RefreshAndExit(CHS_MSG_SCAN_DONE, retVal);
	}

	///< prepare the field for scanning

	retVal = PopulateMachineList();
	if(retVal!=MSR_ERR_OK)
		return RefreshAndExit(CHS_MSG_SCAN_DONE, retVal);

	SendStatusMessage(CHS_MSG_AFTER_PHASE, 2, (LPARAM)machineList.GetSize());

	SendStatusMessage(CHS_MSG_UPDATE_DATA, 0, 0);

	if(stopStopStop) return RefreshAndExit(CHS_MSG_SCAN_DONE, MSR_ERR_USER_STOPPED);

	///< is there any method that needs credentials ?

	credentialsNeeded = false;
	wmiFound = false;
	cnt = config.GetJobCount();
	for(i=0; i<cnt; i++){
		cfgElem = config.GetJobElement(i);
		if(cfgElem!=NULL){
			if(cfgElem->phaseType==__wptScan){
				switch(cfgElem->methodType){
					case __wmtWMI:
						wmiFound = true;
						credentialsNeeded = true;
						break;

					case __wmtShutdown:
						credentialsNeeded = true;
						break;
				}
			}
		}
	}
	if(credentialsNeeded){
		retVal = machineList.GetSize();
		if(retVal>0){
			if(automaticMode) GetProcessCredentials();
			else              GetUserCredentials();
		}

		wmiWorker.SetUserCredentials(userName, userPass, automaticMode);
		scanMethodShutdown.SetUserCredentials(userName, userPass, automaticMode);

		if(wmiFound)
			wmiWorker.InitComAndWmi();
	}

	///< and, finally, Scan

	retVal = ScanPhaseWrapper(__wptScan);

	if(credentialsNeeded){
		if(wmiFound){
			wmiWorker.CleanUpUserCredentials();
			wmiWorker.DisconnectFromServer();
			wmiWorker.CleanUpCom();
		}

		scanMethodShutdown.CleanUpUserCredentials();

		CleanUpUserCredentials();
	}

	if(retVal!=MSR_ERR_OK)
		return RefreshAndExit(CHS_MSG_SCAN_DONE, retVal);

	if(config.xArpCacheOp==__acoUpdate || config.xArpCacheOp==__acoUpdOnly){
		///< rebuild arp cache
		UpdateArpCache();

		if(!applicationPath.IsEmpty()){
			str = applicationPath;
			str += _T("arpCache.dat");
			arpCacheList.SaveToFile(str);
		}
	}

	return RefreshAndExit(CHS_MSG_SCAN_DONE, retVal);
}

void TheScanner::StopScan(void)
{
	stopStopStop = true;

	scanMethodArp.Stop();
	scanMethodPing.Stop();
	scanMethodDns.Stop();
	scanMethodSyn.Stop();
	scanMethodAck.Stop();
	scanMethodFin.Stop();
	scanMethodNull.Stop();
	scanMethodXmas.Stop();
	scanMethodUdp.Stop();
	scanMethodIpProto.Stop();
	scanMethodNetBios.Stop();
	scanMethodWOL.Stop();
	scanMethodSnmp.Stop();
	scanMethodDHCP.Stop();
	scanMethodPromisc.Stop();
	scanMethod4PingBcast.Stop();

	scanMethod6PingBcast.Stop();
	scanMethod6ND.Stop();
	scanMethod6MLD.Stop();
	scanMethod6Ping.Stop();
	scanMethod6Syn.Stop();
	scanMethod6Ack.Stop();
	scanMethod6Fin.Stop();
	scanMethod6Null.Stop();
	scanMethod6Xmas.Stop();
	scanMethod6Udp.Stop();
	scanMethod6Proto.Stop();

	scanMethodWMI.Stop();
	scanMethodIgmp.Stop();
	scanMethodShutdown.Stop();

	scanMethodHunt.Stop();

	scanMethodPassive.Stop();
}

//---------------------------------------------------------------------------

void TheScanner::UpdateArpCache(void)
{
	MMachineInfo* mmi;
	MArpCacheRec rec;
	int i, cnt;

	cnt = machineList.GetSize();
	for(i=0; i<cnt; i++){
		mmi = machineList.GetByIdx(i);
		if(mmi!=NULL){
			if(!mmi->address.gwMAC){
				rec.addrIP.LoadFromPointer(&mmi->address.addrIP);
				rec.addrMAC.LoadFromPointer(&mmi->address.addrMAC);
				arpCacheList.Add(&rec);
			}
		}
	}
}

//---------------------------------------------------------------------------

int TheScanner::Start(WorkCfg* cfgIn)
{
	int res;

	if(mainScanner==NULL) return MSR_ERR_FAILED;

	res = Initialize(cfgIn);
	if(res!=MSR_ERR_OK)
		return res;

	CWinThread* thread;
	DWORD dwRes;
	thread = AfxBeginThread(MainScanner_StartScan, NULL, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED);
	if(thread==NULL)
		return MSR_ERR_FAILED;

	thread->m_bAutoDelete = TRUE;
	dwRes = thread->ResumeThread();
	if(dwRes==0xFFFFFFFF)
		return MSR_ERR_FAILED;

	return MSR_ERR_OK;
}

void TheScanner::Stop(void)
{
	StopScan();
}

bool TheScanner::IsRunning(void)
{
	return isRunning;
}

//---------------------------------------------------------------------------

UINT MainScanner_StartScan(void* param)
{
	if(mainScanner==NULL) return MSR_ERR_FAILED;

	return mainScanner->StartScan();
}

//---------------------------------------------------------------------------

int TheScanner::GetMachInfoCount(void)
{
	return machineList.GetSize();
}

MMachineInfo* TheScanner::GetMachInfo(int idx)
{
	return machineList.GetByIdx(idx);
}

MMachineInfoList* TheScanner::GetMachInfoList(void)
{
	return &machineList;
}

//---------------------------------------------------------------------------

CString TheScanner::GetStartTime(void)
{
	CString str;
	char timeBuff[26];
	errno_t err;

	str.Empty();
	if(startTime==0) return str;

	err = _ctime64_s(timeBuff, 26, &startTime);
	if(err==0){
		str = timeBuff;
		str = str.Left(24);
	}

	return str;
}

CString TheScanner::GetNameFromTime(void)
{
	CString str;
	errno_t err;
	struct tm tms;

	if(startTime==0){
		str = "unnamed";
		return str;
	}

	err = _localtime64_s(&tms, &startTime);
	if(err!=0){
		str = _T("unnamed");
		return str;
	}

	str.Format(_T("CHScanResult_%d-%02d-%02d_%02d-%02d-%02d"),
		1900 + tms.tm_year, tms.tm_mon + 1, tms.tm_mday,
		tms.tm_hour, tms.tm_min, tms.tm_sec);
	return str;
}

//---------------------------------------------------------------------------

CString TheScanner::GetLocalIPv4AddressAsString(void)
{
	CString str;
	in_addr ia;

	winPcap.defaultInterfaceName = defaultInterfaceName;
	if(!winPcap.GetIpInterface()){
		str = _T("-");
		return str;
	}

	ia.S_un.S_addr = winPcap.GetLocalIP();
	str = inet_ntoa(ia);
	return str;
}

CString TheScanner::GetLocalIPv6AddressAsString(void)
{
	CString str;
	MAddressIPv6 mai6;

	in6_addr ia6;
	winPcap.GetIPv6UnicastAddress(&ia6);
	mai6.Set(&ia6);
	if(mai6.IsEmpty()){
		str = _T("-");
		return str;
	}

	str = mai6.GetS();
	return str;
}

CString TheScanner::GetInterfaceName(void)
{
	return winPcap.GetIpInterfaceName();
}

CString TheScanner::GetIpInterfaceAdapterName(void)
{
	return winPcap.GetIpInterfaceAdapterName();
}

int TheScanner::GetIpInterfaceCount(void)
{
	return winPcap.GetIpInterfaceCount();
}

MIpInterfaceInfo* TheScanner::GetIpInterfaceInfo(int idx)
{
	return winPcap.GetIpInterfaceInfo(idx);
}

//---------------------------------------------------------------------------

DWORD TheScanner::GetLocalIP(void)
{
	return winPcap.GetLocalIP();
}

DWORD TheScanner::GetLocalIPMask(void)
{
	return winPcap.GetLocalIPMask();
}

//---------------------------------------------------------------------------

MWinPcap* TheScanner::GetWinPcap(void)
{
	return &winPcap;
}

//---------------------------------------------------------------------------

bool TheScanner::GetUserCredentials(void)
{
	CREDUI_INFO cui;
	BOOL saveInfo;
	DWORD res;

	CleanUpUserCredentials();

	cui.cbSize = sizeof(CREDUI_INFO);
	cui.hwndParent = NULL;
	cui.pszMessageText = TEXT("Credentials");
	cui.pszCaptionText = TEXT("Account Informations");
	cui.hbmBanner = NULL;

	saveInfo = FALSE;

	res = CredUIPromptForCredentials(
			&cui,
			TEXT(""),
			NULL,
			0,
			userName,
			CREDUI_MAX_USERNAME_LENGTH + 1,
			userPass,
			CREDUI_MAX_PASSWORD_LENGTH + 1,
			&saveInfo,
			CREDUI_FLAGS_GENERIC_CREDENTIALS | CREDUI_FLAGS_ALWAYS_SHOW_UI | CREDUI_FLAGS_DO_NOT_PERSIST
			);

	SecureZeroMemory(&authIdentity, sizeof(authIdentity));

#ifdef _UNICODE
	authIdentity.User           = (unsigned short*)userName;
	authIdentity.UserLength     = (unsigned long)_tcsnlen(userPass, CREDUI_MAX_USERNAME_LENGTH);
	authIdentity.Domain         = NULL;
	authIdentity.DomainLength   = 0;
	authIdentity.Password       = (unsigned short*)userPass;
	authIdentity.PasswordLength = (unsigned long)_tcsnlen(userPass, CREDUI_MAX_PASSWORD_LENGTH);
	authIdentity.Flags          = SEC_WINNT_AUTH_IDENTITY_UNICODE;
#else
	authIdentity.User           = (unsigned char*)userName;
	authIdentity.UserLength     = (unsigned long)_tcsnlen(userPass, CREDUI_MAX_USERNAME_LENGTH);
	authIdentity.Domain         = NULL;
	authIdentity.DomainLength   = 0;
	authIdentity.Password       = (unsigned char*)userPass;
	authIdentity.PasswordLength = (unsigned long)_tcsnlen(userPass, CREDUI_MAX_PASSWORD_LENGTH);
	authIdentity.Flags          = SEC_WINNT_AUTH_IDENTITY_ANSI;
#endif

	return res==NO_ERROR ? true : false;
}

bool TheScanner::GetProcessCredentials(void)
{
	CleanUpUserCredentials();
	
	SecureZeroMemory(&authIdentity, sizeof(authIdentity));

#ifdef _UNICODE
	authIdentity.User           = NULL;
	authIdentity.UserLength     = 0;
	authIdentity.Domain         = NULL;
	authIdentity.DomainLength   = 0;
	authIdentity.Password       = NULL;
	authIdentity.PasswordLength = 0;
	authIdentity.Flags          = SEC_WINNT_AUTH_IDENTITY_UNICODE;
#else
	authIdentity.User           = NULL;
	authIdentity.UserLength     = 0;
	authIdentity.Domain         = NULL;
	authIdentity.DomainLength   = 0;
	authIdentity.Password       = NULL;
	authIdentity.PasswordLength = 0;
	authIdentity.Flags          = SEC_WINNT_AUTH_IDENTITY_ANSI;
#endif

	return true;
}

void TheScanner::CleanUpUserCredentials(void)
{
	SecureZeroMemory(userName, (CREDUI_MAX_USERNAME_LENGTH + 1) * sizeof(TCHAR));
	SecureZeroMemory(userPass, (CREDUI_MAX_PASSWORD_LENGTH + 1) * sizeof(TCHAR));
	SecureZeroMemory(&authIdentity, sizeof(authIdentity));
}

//---------------------------------------------------------------------------

MArpCacheList* TheScanner::GetArpCacheList(void)
{
	return &arpCacheList;
}

//---------------------------------------------------------------------------
